{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5c0122e65c053f38",
   "metadata": {},
   "source": [
    "# Outbound Auth\n",
    "\n",
    "Outbound Auth allows agents and the AgentCore Gateway to securely access AWS resources and third-party services on behalf of users who have been authenticated and authorized during Inbound Auth. To integrate authorization with an AWS resource or third-party service, it's necessary to configure both Inbound Auth and Outbound Auth.\n",
    "\n",
    "With just-enough access and secure permission delegation supported by AgentCore Identity, agents can seamlessly and securely access AWS resources and third-party tools such as GitHub, Google, Salesforce, and Slack. Agents can perform actions on these services either on behalf of users or independently, provided there is pre-authorized user consent. Additionally, you can reduce consent fatigue using a secure token vault and create streamlined AI agent experiences.\n",
    "\n",
    "## Outbound Authentication Configuration\n",
    "\n",
    "First, you register your client application with third-party providers and then create an Outbound Auth. You specify how you want to validate access to the AWS resource or third-party service or AgentCore Gateway targets. You can use OAuth 2LO/3LO or API keys. With OAuth, you can select from providers that AgentCore Identity provides. In which case you enter the configuration details for the providers from AgentCore Identity. Alternatively, you can supply details for a custom provider. \n",
    "\n",
    "When a user wants access to an AWS resource or third-party service or AgentCore Gateway target, the Outbound Auth confirms that the access tokens provided by Incoming Auth are valid and if so, allows access to the resource.\n",
    "\n",
    "<div style=\"text-align:center\">\n",
    "    <img src=\"images/outbound_auth.png\" width=\"90%\"/>\n",
    "</div>\n",
    "\n",
    "## Resource credential providers\n",
    "\n",
    "This is a component that agent code uses to retrieve credentials of downstream resource servers (e.g., Google, GitHub) to access them, e.g., fetch emails from Gmail, add a meeting to Google Calendar.  It removes the heavy-lifting of agent developers implementing 2LO and 3LO OAuth2 orchestration flows across end-users, agent code, and external authorization servers. AgentCore provides both a custom OAuth2 credential provider and a list of built-in providers such Google, GitHub, Slack, Salesforce with authorization server endpoint and provider-specific parameters pre-filled.\n",
    "  \n",
    "\n",
    "Bedrock AgentCore Identity provides OAuth2 and API Key Credential Providers for agent developers to authenticate with external resources that support OAuth2 or API key. In the following example, we will walk you through configuring an API Key credential provider.  An agent can then use the API Key credential provider to retrieve the API key for any agent operations. Please refer to the documentation for the other credential providers.\n",
    "\n",
    " ### Creating a resource credential provider.\n",
    "\n",
    " Here is an example of creating a API Key resource credential provider.\n",
    "\n",
    "```\n",
    "from bedrock_agentcore.services.identity import IdentityClient\n",
    "identity_client = IdentityClient(region=\"us-west-2\")\n",
    "\n",
    "api_key_provider = identity_client.create_api_key_credential_provider({\n",
    "    \"name\": \"APIKey-provider-name\",\n",
    "    \"apiKey\": \"<my-api-key>\" # Replace it with the API key you obtain from the external application vendor, e.g., OpenAI\n",
    "})\n",
    "print(api_key_provider)\n",
    "```\n",
    "\n",
    " ### Retrieving access tokens or API keys from the Resource credential provider.\n",
    "\n",
    "Here is an example of retrieving the API key from the API Key credential provider. The agent can use the API key to interact with services like a LLM or other services that use an API key configuration. In order to retrieve credentials like access_token or API key from the credential provider, you can decorate your function as shown below.\n",
    "\n",
    "```\n",
    "import asyncio\n",
    "from bedrock_agentcore.identity.auth import requires_access_token, requires_api_key\n",
    "\n",
    "@requires_api_key(\n",
    "    provider_name=\"APIKey-provider\" # replace with your own credential provider name\n",
    ")\n",
    "async def need_api_key(*, api_key: str):\n",
    "    print(f'received api key for async func: {api_key}')\n",
    "\n",
    "await need_api_key(api_key=\"\")\n",
    "```\n",
    "\n",
    "Here are the various parameters you can use with the @require_access_token decorator.\n",
    "\n",
    "\n",
    "| Parameter Name      | Description                                                              |\n",
    "|:--------------------|:-------------------------------------------------------------------------|\n",
    "| provider_name       | The credential provider name                                             |\n",
    "| into                | Parameter name to inject the token into                                  |\n",
    "| scopes              | OAuth2 scopes to request                                                 |\n",
    "| on_auth_url\t      | Callback for handling authorization URLs                                 |\n",
    "| auth_flow           | Authentication flow type (\"M2M\" or \"USER_FEDERATION\")                    |\n",
    "| callback_url        | OAuth2 callback URL                                                      |\n",
    "| force_authentication| Force re-authentication                                                  |\n",
    "| token_poller        | Custom token poller implementation                                       |\n",
    "\n",
    "\t\t\n",
    "\n",
    "\n",
    "# Hosting Strands Agents with OpenAI models in Amazon Bedrock AgentCore Runtime\n",
    "\n",
    "## Overview\n",
    "\n",
    "\n",
    "In this tutorial we will modify the agent you deployed in 01-AgentCore-runtime, that uses openai-model and configure it for Outbound Auth using the API Key credential provider. You will set up an API Key credential provider to store the open-ai key and modify the agent code to use this key.\n",
    "\n",
    "### Tutorial Architecture\n",
    "\n",
    "<div style=\"text-align:center\">\n",
    "    <img src=\"images/outbound_auth_api.png\" width=\"90%\"/>\n",
    "</div>\n",
    "\n",
    "\n",
    "### Tutorial Details\n",
    "\n",
    "| Information         | Details                                                                  |\n",
    "|:--------------------|:-------------------------------------------------------------------------|\n",
    "| Tutorial type       | Conversational                                                           |\n",
    "| Agent type          | Single                                                                   |\n",
    "| Agentic Framework   | Strands Agents                                                           |\n",
    "| LLM model           | GPT 4.1 mini                                                             |\n",
    "| Tutorial components | Hosting agent on AgentCore Runtime. Using Strands Agent and OpenAI Model |\n",
    "| Tutorial vertical   | Cross-vertical                                                           |\n",
    "| Example complexity  | Easy                                                                     |\n",
    "| SDK used            | Amazon BedrockAgentCore Python SDK and boto3                             |\n",
    "| Credential Provider | Type : API Key                                                           |\n",
    "\n",
    "\n",
    "### Tutorial Key Features\n",
    "\n",
    "* Hosting Agents on Amazon Bedrock AgentCore Runtime\n",
    "* Using OpenAI models\n",
    "* Using Strands Agents\n",
    "* Using AgentCore egress Auth with API Key credential provider.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a676f58ecf52b42",
   "metadata": {},
   "source": [
    "## Prerequisites\n",
    "\n",
    "To execute this tutorial you will need:\n",
    "* Python 3.10+\n",
    "* AWS credentials\n",
    "* Amazon Bedrock AgentCore SDK\n",
    "* Strands Agents\n",
    "* Docker running"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "initial_id",
   "metadata": {
    "jupyter": {
     "is_executing": true
    }
   },
   "outputs": [],
   "source": [
    "!uv add -r requirements.txt --active"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca924a7a2731e26f",
   "metadata": {},
   "source": [
    "## Creating your agents and experimenting locally\n",
    "\n",
    "Before we deploy our agents to AgentCore Runtime, let's develop and run them locally for experimentation purposes.\n",
    "\n",
    "For production agentic applications we will need to decouple the agent creation process from the agent invocation one. With AgentCore Runtime, we will decorate the invocation part of our agent with the `@app.entrypoint` decorator and have it as the entry point for our runtime. Let's first look how each agent is developed during the experimentation phase.\n",
    "\n",
    "The architecture here will look as following:\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/architecture_local.png\" width=\"50%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7d386ab54e85e63",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile strands_agents_openai.py\n",
    "from strands import Agent, tool\n",
    "from strands_tools import calculator # Import the calculator tool\n",
    "import argparse\n",
    "import json\n",
    "from strands.models.litellm import LiteLLMModel\n",
    "import os\n",
    "\n",
    "##Update the below configuration with your Azure API Key details.\n",
    "os.environ[\"AZURE_API_KEY\"] = \"<YOUR_API_KEY>\"\n",
    "os.environ[\"AZURE_API_BASE\"] = \"<YOUR_API_BASE>\"\n",
    "os.environ[\"AZURE_API_VERSION\"] = \"<YOUR_API_VERSION>\"\n",
    "\n",
    "# Create a custom tool \n",
    "@tool\n",
    "def weather():\n",
    "    \"\"\" Get weather \"\"\" # Dummy implementation\n",
    "    return \"sunny\"\n",
    "\n",
    "model = \"azure/gpt-4.1-mini\"\n",
    "litellm_model = LiteLLMModel(\n",
    "    model_id=model, params={\"max_tokens\": 32000, \"temperature\": 0.7}\n",
    ")\n",
    "\n",
    "\n",
    "agent = Agent(\n",
    "    model=litellm_model,\n",
    "    tools=[calculator, weather],\n",
    "    system_prompt=\"You're a helpful assistant. You can do simple math calculation, and tell the weather.\"\n",
    ")\n",
    "\n",
    "def strands_agent_open_ai(payload):\n",
    "    \"\"\"\n",
    "    Invoke the agent with a payload\n",
    "    \"\"\"\n",
    "    user_input = payload.get(\"prompt\")\n",
    "    response = agent(user_input)\n",
    "    return response.message['content'][0]['text']\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    parser = argparse.ArgumentParser()\n",
    "    parser.add_argument(\"payload\", type=str)\n",
    "    args = parser.parse_args()\n",
    "    response = strands_agent_open_ai(json.loads(args.payload))\n",
    "    print(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68499675-db8d-47c6-8c0c-5d66dcb06229",
   "metadata": {},
   "source": [
    "#### Invoking local agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1226d59e6b56c96",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-29T21:52:06.461281Z",
     "start_time": "2025-06-29T21:52:06.456854Z"
    }
   },
   "outputs": [],
   "source": [
    "!python strands_agents_openai.py '{\"prompt\": \"What is the weather now?\"}'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5021c377",
   "metadata": {},
   "source": [
    "## Create a Resource Credential Provider"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "08daa5b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "from bedrock_agentcore.services.identity import IdentityClient\n",
    "\n",
    "from boto3.session import Session\n",
    "import boto3\n",
    "boto_session = Session()\n",
    "region = boto_session.region_name\n",
    "\n",
    "#Configure API Key Provider\n",
    "identity_client = IdentityClient(region=region)\n",
    "\n",
    "api_key_provider = identity_client.create_api_key_credential_provider({\n",
    "    \"name\": \"openai-apikey-provider\",\n",
    "    \"apiKey\": \"<YOUR_API_KEY>\" # Replace it with the API key you obtain from the external application vendor, e.g., OpenAI\n",
    "})\n",
    "print(api_key_provider)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "932110e6-fca6-47b6-b7c5-c4714a866a80",
   "metadata": {},
   "source": [
    "## Preparing your agent for deployment on AgentCore Runtime and use the Resource Credential Provider\n",
    "\n",
    "Let's now deploy our agents to AgentCore Runtime. To do so we need to:\n",
    "* Import the Runtime App with `from bedrock_agentcore.runtime import BedrockAgentCoreApp`\n",
    "* Initialize the App in our code with `app = BedrockAgentCoreApp()`\n",
    "* Decorate the invocation function with the `@app.entrypoint` decorator\n",
    "* Let AgentCoreRuntime control the running of the agent with `app.run()`\n",
    "* Retrieve the openAI key from the resource credential provider created in the previous step\n",
    "\n",
    "### Strands Agents with OpenAI model\n",
    "Let's start with our Strands Agent using the GPT 4.1 mini model. All the others will work exactly the same."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b845b32-a03e-45c2-a2f0-2afba8069f47",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile strands_agents_openai.py\n",
    "import asyncio\n",
    "from bedrock_agentcore.identity.auth import requires_access_token, requires_api_key\n",
    "from strands import Agent, tool\n",
    "from strands_tools import calculator \n",
    "import argparse\n",
    "import json\n",
    "from strands.models.litellm import LiteLLMModel\n",
    "import os\n",
    "from bedrock_agentcore.runtime import BedrockAgentCoreApp\n",
    "\n",
    "AZURE_API_KEY_FROM_CREDS_PROVIDER = \"\"\n",
    "\n",
    "\n",
    "@requires_api_key(\n",
    "    provider_name=\"openai-apikey-provider\" # replace with your own credential provider name\n",
    ")\n",
    "async def need_api_key(*, api_key: str):\n",
    "    global AZURE_API_KEY_FROM_CREDS_PROVIDER\n",
    "    print(f'received api key for async func: {api_key}')\n",
    "    AZURE_API_KEY_FROM_CREDS_PROVIDER = api_key\n",
    "\n",
    "# Don't print empty value at module level - will print in entrypoint function\n",
    "\n",
    "app = BedrockAgentCoreApp()\n",
    "\n",
    "# API key will be set dynamically in the entrypoint function\n",
    "#Update the below configuration with your Azure API Key details.\n",
    "os.environ[\"AZURE_API_BASE\"] = \"<YOUR_API_BASE>\"\n",
    "os.environ[\"AZURE_API_VERSION\"] = \"<YOUR_API_VERSION>\"\n",
    "\n",
    "# Create a custom tool \n",
    "@tool\n",
    "def weather():\n",
    "    \"\"\" Get weather \"\"\" # Dummy implementation\n",
    "    return \"sunny\"\n",
    "\n",
    "# Global agent variable\n",
    "agent = None\n",
    "\n",
    "@app.entrypoint\n",
    "async def strands_agent_open_ai(payload):\n",
    "    \"\"\"\n",
    "    Invoke the agent with a payload\n",
    "    \"\"\"\n",
    "    global AZURE_API_KEY_FROM_CREDS_PROVIDER, agent\n",
    "    \n",
    "    print(f\"Entrypoint called with AZURE_API_KEY_FROM_CREDS_PROVIDER: '{AZURE_API_KEY_FROM_CREDS_PROVIDER}'\")\n",
    "    \n",
    "    # Get API key if not already retrieved\n",
    "    if not AZURE_API_KEY_FROM_CREDS_PROVIDER:\n",
    "        print(\"Attempting to retrieve API key...\")\n",
    "        try:\n",
    "            await need_api_key(api_key=\"\")\n",
    "            print(f\"API key retrieved: '{AZURE_API_KEY_FROM_CREDS_PROVIDER}'\")\n",
    "            os.environ[\"AZURE_API_KEY\"] = AZURE_API_KEY_FROM_CREDS_PROVIDER\n",
    "            print(\"Environment variable AZURE_API_KEY set\")\n",
    "        except Exception as e:\n",
    "            print(f\"Error retrieving API key: {e}\")\n",
    "            raise\n",
    "    else:\n",
    "        print(\"API key already available\")\n",
    "    \n",
    "    # Initialize agent after API key is set\n",
    "    if agent is None:\n",
    "        print(\"Initializing agent with API key...\")\n",
    "        model = \"azure/gpt-4.1-mini\"\n",
    "        litellm_model = LiteLLMModel(\n",
    "            model_id=model, params={\"max_tokens\": 32000, \"temperature\": 0.7}\n",
    "        )\n",
    "        \n",
    "        agent = Agent(\n",
    "            model=litellm_model,\n",
    "            tools=[calculator, weather],\n",
    "            system_prompt=\"You're a helpful assistant. You can do simple math calculation, and tell the weather.\"\n",
    "        )\n",
    "        print(\"Agent initialized successfully\")\n",
    "    \n",
    "    user_input = payload.get(\"prompt\")\n",
    "    print(f\"User input: {user_input}\")\n",
    "    \n",
    "    try:\n",
    "        response = agent(user_input)\n",
    "        print(f\"Agent response: {response}\")\n",
    "        return response.message['content'][0]['text']\n",
    "    except Exception as e:\n",
    "        print(f\"Error in agent processing: {e}\")\n",
    "        raise\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    app.run()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c64db7b5-0f1b-475f-9bf2-467b4449d46a",
   "metadata": {},
   "source": [
    "## What happens behind the scenes?\n",
    "\n",
    "When you use `BedrockAgentCoreApp`, it automatically:\n",
    "\n",
    "* Creates an HTTP server that listens on the port 8080\n",
    "* Implements the required `/invocations` endpoint for processing the agent's requirements\n",
    "* Implements the `/ping` endpoint for health checks (very important for asynchronous agents)\n",
    "* Handles proper content types and response formats\n",
    "* Manages error handling according to the AWS standards"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6820ca8f-a8a8-4f34-b4ef-b6dad3776261",
   "metadata": {},
   "source": [
    "## Deploying the agent to AgentCore Runtime\n",
    "\n",
    "The `CreateAgentRuntime` operation supports comprehensive configuration options, letting you specify container images, environment variables and encryption settings. You can also configure protocol settings (HTTP, MCP) and authorization mechanisms to control how your clients communicate with the agent. \n",
    "\n",
    "**Note:** Operations best practice is to package code as container and push to ECR using CI/CD pipelines and IaC\n",
    "\n",
    "In this tutorial can will the Amazon Bedrock AgentCode Python SDK to easily package your artifacts and deploy them to AgentCore runtime."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0861401-a111-4ade-9e02-50f52fdfa9b1",
   "metadata": {},
   "source": [
    "### Creating runtime role\n",
    "\n",
    "Before starting, let's create an IAM role for our AgentCore Runtime. We will do so using the utils function pre-developed for you."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54dd2fdf-985c-4a70-8b87-071783a209de",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "\n",
    "# Get the current notebook's directory\n",
    "current_dir = os.path.dirname(os.path.abspath('__file__' if '__file__' in globals() else '.'))\n",
    "\n",
    "# Navigate up to the utils.py location\n",
    "utils_dir = os.path.join(current_dir, '..')\n",
    "utils_dir = os.path.abspath(utils_dir)\n",
    "\n",
    "# Add to sys.path\n",
    "sys.path.insert(0, utils_dir)\n",
    "\n",
    "from utils import create_agentcore_role\n",
    "\n",
    "agent_name=\"strands_agents_openai\"\n",
    "agentcore_iam_role = create_agentcore_role(agent_name=agent_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8855aceb-b79f-4aaa-b16f-8577c059816a",
   "metadata": {},
   "source": [
    "### Configure AgentCore Runtime deployment\n",
    "\n",
    "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n",
    "\n",
    "During the configure step, your docker file will be generated based on your application code\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/configure.png\" width=\"40%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2e79eba2-ca59-463f-9ebf-56e362d7ae66",
   "metadata": {},
   "outputs": [],
   "source": [
    "from bedrock_agentcore_starter_toolkit import Runtime\n",
    "from boto3.session import Session\n",
    "import boto3\n",
    "import json\n",
    "boto_session = Session()\n",
    "region = boto_session.region_name\n",
    "region\n",
    "\n",
    "agentcore_runtime = Runtime()\n",
    "agent_name=\"strands_agents_openai\"\n",
    "\n",
    "response = agentcore_runtime.configure(\n",
    "    entrypoint=\"strands_agents_openai.py\",\n",
    "    execution_role=agentcore_iam_role['Role']['Arn'],\n",
    "    auto_create_ecr=True,\n",
    "    agent_name=agent_name,\n",
    "    requirements_file=\"requirements.txt\",\n",
    "    region=region\n",
    ")\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e1b84cc-798e-472c-ac0b-2c315f4b704d",
   "metadata": {},
   "source": [
    "### Launching agent to AgentCore Runtime\n",
    "\n",
    "Now that we've got a docker file, let's launch the agent to the AgentCore Runtime. This will create the Amazon ECR repository and the AgentCore Runtime\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/launch.png\" width=\"75%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17a32ab8-7701-4900-8055-e24364bdf35c",
   "metadata": {},
   "outputs": [],
   "source": [
    "launch_result = agentcore_runtime.launch()\n",
    "launch_result"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a0ae9c09-09db-4a76-871a-92eacd96b9c3",
   "metadata": {},
   "source": [
    "### Checking for the AgentCore Runtime Status\n",
    "Now that we've deployed the AgentCore Runtime, let's check for it's deployment status"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "afa6ac09-9adb-4846-9fc1-4d12aeb74853",
   "metadata": {},
   "outputs": [],
   "source": [
    "status_response = agentcore_runtime.status()\n",
    "status = status_response.endpoint['status']\n",
    "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n",
    "while status not in end_status:\n",
    "    time.sleep(10)\n",
    "    status_response = agentcore_runtime.status()\n",
    "    status = status_response.endpoint['status']\n",
    "    print(status)\n",
    "status"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7f89c56-918a-4cab-beaa-c7ac43a2ba29",
   "metadata": {},
   "source": [
    "### Invoking AgentCore Runtime\n",
    "\n",
    "Finally, we can invoke our AgentCore Runtime with a payload\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/invoke.png\" width=75%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d909e42-e1a0-407f-84c2-3d16cc889cd3",
   "metadata": {},
   "outputs": [],
   "source": [
    "invoke_response = agentcore_runtime.invoke({\"prompt\": \"Hello\"}, user_id=\"userid_1234567890\")\n",
    "invoke_response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fefa09f2-d25a-483f-aedb-11690bb8923a",
   "metadata": {},
   "source": [
    "### Processing invocation results\n",
    "\n",
    "We can now process our invocation results to include it in an application"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "11249103-cfb3-47b5-970d-981a977a225a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.display import Markdown, display\n",
    "response_text = json.loads(invoke_response['response'][0].decode(\"utf-8\"))\n",
    "display(Markdown(response_text))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c1d2bce-be41-478c-8bed-b4037c385795",
   "metadata": {},
   "source": [
    "### Invoking AgentCore Runtime with boto3\n",
    "\n",
    "Now that your AgentCore Runtime was created you can invoke it with any AWS SDK. For instance, you can use the boto3 `invoke_agent_runtime` method for it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f84e68d-6c04-41b9-bf5b-60edc3fa0985",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_arn = launch_result.agent_arn\n",
    "agentcore_client = boto3.client(\n",
    "    'bedrock-agentcore',\n",
    "    region_name=region\n",
    ")\n",
    "\n",
    "boto3_response = agentcore_client.invoke_agent_runtime(\n",
    "    agentRuntimeArn=agent_arn,\n",
    "    runtimeUserId=\"userid_1234567890\",\n",
    "    qualifier=\"DEFAULT\",\n",
    "    payload=json.dumps({\"prompt\": \"How much is 2X2?\"})\n",
    ")\n",
    "if \"text/event-stream\" in boto3_response.get(\"contentType\", \"\"):\n",
    "    content = []\n",
    "    for line in boto3_response[\"response\"].iter_lines(chunk_size=1):\n",
    "        if line:\n",
    "            line = line.decode(\"utf-8\")\n",
    "            if line.startswith(\"data: \"):\n",
    "                line = line[6:]\n",
    "                logger.info(line)\n",
    "                content.append(line)\n",
    "    display(Markdown(\"\\n\".join(content)))\n",
    "else:\n",
    "    try:\n",
    "        events = []\n",
    "        for event in boto3_response.get(\"response\", []):\n",
    "            events.append(event)\n",
    "    except Exception as e:\n",
    "        events = [f\"Error reading EventStream: {e}\"]\n",
    "    display(Markdown(json.loads(events[0].decode(\"utf-8\"))))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d3fdfe404469632",
   "metadata": {},
   "source": [
    "## Cleanup (Optional)\n",
    "\n",
    "Let's now clean up the AgentCore Runtime created"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76a6cf1416830a54",
   "metadata": {},
   "outputs": [],
   "source": [
    "agentcore_control_client = boto3.client(\n",
    "    'bedrock-agentcore-control',\n",
    "    region_name=region\n",
    ")\n",
    "ecr_client = boto3.client(\n",
    "    'ecr',\n",
    "    region_name=region\n",
    "    \n",
    ")\n",
    "\n",
    "iam_client = boto3.client('iam')\n",
    "\n",
    "runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n",
    "    agentRuntimeId=launch_result.agent_id\n",
    ")\n",
    "\n",
    "response = ecr_client.delete_repository(\n",
    "    repositoryName=launch_result.ecr_uri.split('/')[1],\n",
    "    force=True\n",
    ")\n",
    "\n",
    "policies = iam_client.list_role_policies(\n",
    "    RoleName=agentcore_iam_role['Role']['RoleName'],\n",
    "    MaxItems=100\n",
    ")\n",
    "\n",
    "for policy_name in policies['PolicyNames']:\n",
    "    iam_client.delete_role_policy(\n",
    "        RoleName=agentcore_role_name,\n",
    "        PolicyName=policy_name\n",
    "    )\n",
    "iam_response = iam_client.delete_role(\n",
    "    RoleName=agentcore_role_name\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b118ad38-feeb-4d1d-9d57-e5c845becc56",
   "metadata": {},
   "source": [
    "# Congratulations!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
